package ru.nosport.matrixaria.core;

import org.apache.commons.configuration.Configuration;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.HierarchicalConfiguration;
import org.apache.commons.configuration.XMLConfiguration;

import java.io.IOException;
import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.FileHandler;
import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.Logger;

/**
 * User: vfabr
 * Date: 04.10.2006
 * Time: 16:20:56
 */
public class Init {

	private String confPath;
	private List<ModuleShortcut> allModules = new ArrayList<ModuleShortcut>();

	/**
	 * @param _confPath -
	 * @throws InitException -
	 */
	public Init(String _confPath) throws InitException {
		this.confPath = _confPath;
		this.init();
	}

	/**
	 * @throws InitException -
	 */
	private void init() throws InitException {
		File confFile = new File(this.confPath);
		if(!confFile.exists()){
			throw new InitException("Main configuration "+this.confPath+" not found!");
		}

		try {
			HierarchicalConfiguration conf = new XMLConfiguration(this.confPath);

			CoreVars.init(conf, confFile.getParent());
			CoreVars vars = CoreVars.getInstance();

			String localPath = vars.homePath;

			this.crtLogs();

			InitConfiguration coreConf = new InitConfiguration(new XMLConfiguration(localPath+vars.coreModulesConf), localPath+vars.coreModuleConfPath);
			List<ModuleShortcut> coreModules = coreConf.getModuleShortcutList();

			InitConfiguration userConf = new InitConfiguration(new XMLConfiguration(localPath+vars.userModulesConf), localPath+vars.userModuleConfPath);
			List<ModuleShortcut> userModules = userConf.getModuleShortcutList();

			InitConfiguration remoteConf = new InitConfiguration(new XMLConfiguration(localPath+vars.remoteModulesConf), localPath+vars.remoteModuleConfPath);
			List<ModuleShortcut> remoteModules = remoteConf.getModuleShortcutList();

			this.allModules = new ArrayList<ModuleShortcut>(coreModules);
			this.allModules.addAll(remoteModules);
			this.allModules.addAll(userModules);

			MLogger.setModules(this.allModules);

			this.bootModules(this.allModules);
		} catch (ConfigurationException _ex) {
			this.unInit();
			throw new InitException(_ex);
		} catch (IOException _ex) {
			throw new InitException("Can't create system logs. Please check configuration files.\n" + _ex.getMessage(), _ex);
		} catch (CoreException _ex) {
			throw new InitException(_ex);
		} catch(InitException _ex) {
			this.unInit();
			throw new InitException(_ex);
		}
	}

	/**
	 * @param modules -
	 * @throws InitException -
	 */
	private void bootModules(List<ModuleShortcut> modules) throws InitException {
		for (ModuleShortcut module : modules) {
			if(module.isAutorun()){
				Init.loadModule(module);
			}else if (module.isOnFly()){
				Init.loadOnFlyModule(module);
			}
		}
	}

	/**
	 *
	 * @param _module -
	 * @throws InitException -
	 */
	static protected void loadOnFlyModule(ModuleShortcut _module) throws InitException {
		try {
			Registry.getInstance().loadOnFlyModule(_module.getInstanceName(), _module);
		} catch (RegistryException e) {
			throw new InitException(e);
		}
	}

	/**
	 *
	 * @param _module -
	 * @throws InitException -
	 */
	static protected void loadModule(ModuleShortcut _module) throws InitException{
		Object object;
		Class[] argsClass = new Class[]{Configuration.class};
		Constructor constructor;

		String name = "instance "+_module.getInstanceName()+", class "+_module.getInstanceClass().getName()+" ";
		try {
			Object[] args = new Object[]{_module.getConfig()};
			constructor = _module.getInstanceClass().getConstructor(argsClass);
			object = constructor.newInstance(args);
			Registry.getInstance().loadModule(_module.getInstanceName(), object);
		} catch (NoSuchMethodException _ex) 	{throw new InitException(name, _ex);
		} catch (InvocationTargetException _ex) {throw new InitException(name, _ex);
		} catch (InstantiationException _ex) 	{throw new InitException(name, _ex);
		} catch (IllegalAccessException _ex) 	{throw new InitException(name, _ex);
		} catch (RegistryException _ex) 		{throw new InitException(name, _ex);
		}
	}

	/**
	 *
	 * @throws IOException -
	 * @throws CoreException -
	 */
	private void crtLogs() throws IOException, CoreException {
		LogManager lman = LogManager.getLogManager();
		CoreVars vars = CoreVars.getInstance();

		Logger coreLog = Logger.getLogger(vars.coreLogName);
		Logger coreModuleLog = Logger.getLogger(vars.coreModuleLogName);
		Logger userModuleLog = Logger.getLogger(vars.userModuleLogName);
		Logger remoteModuleLog = Logger.getLogger(vars.remoteModuleLogName);

		coreLog.setLevel(Level.ALL);
		coreModuleLog.setLevel(Level.ALL);
		userModuleLog.setLevel(Level.ALL);
		remoteModuleLog.setLevel(Level.ALL);

		File logDir = new File(vars.logPath);
		if(!logDir.exists()){
			Boolean success = logDir.mkdirs();
			if(success){
				coreLog.info("Log path "+vars.logPath+" not found! Directory created.");
			}else{
				coreLog.warning("Log path "+vars.logPath+" not found! Directory is not created!");
			}
		}

		String cln = vars.logPath + "/" + vars.coreLogName + ".log";
		String cmln = vars.logPath + "/" + vars.coreModuleLogName + ".log";
		String umln = vars.logPath + "/" + vars.userModuleLogName + ".log";
		String rmln = vars.logPath + "/" + vars.remoteModuleLogName + ".log";

		FileHandler cfh = new FileHandler(cln, 0, 1, true);
		FileHandler cmfh = new FileHandler(cmln, 0, 1, true);
		FileHandler umfh = new FileHandler(umln, 0, 1, true);
		FileHandler rmfh = new FileHandler(rmln, 0, 1, true);

		cfh.setLevel(Level.ALL);
		cmfh.setLevel(Level.ALL);
		umfh.setLevel(Level.ALL);
		rmfh.setLevel(Level.ALL);

		cfh.setFormatter(new MatrixariaXMLFormater());
		cmfh.setFormatter(new MatrixariaXMLFormater());
		umfh.setFormatter(new MatrixariaXMLFormater());
		rmfh.setFormatter(new MatrixariaXMLFormater());

		coreLog.addHandler(cfh);
		coreModuleLog.addHandler(cmfh);
		userModuleLog.addHandler(umfh);
		remoteModuleLog.addHandler(rmfh);

		lman.addLogger(coreLog);
		lman.addLogger(coreModuleLog);
		lman.addLogger(userModuleLog);
		lman.addLogger(remoteModuleLog);
	}

	/**
	 * @throws InitException -
	 */
	public void unInit() throws InitException {

		StringBuffer errMess = new StringBuffer("");
		Registry reg = Registry.getInstance();
		for (int i = this.allModules.size() - 1; i >= 0; i--) {
			String instName = this.allModules.get(i).getInstanceName();
			reg.unloadOnFlyModule(instName);
			if (reg.existsModule(instName)) {
				try {
					reg.unloadModule(instName);

				} catch (RegistryException _ex) {
					errMess.append(_ex.toString()).append(" ");
					try {
						Object module = reg.getModule(instName);
						reg.releaseModule(instName, module);
						reg.releaseModule(instName, module);
					} catch (RegistryException _exReg) {
						errMess.append(_exReg.toString()).append("\n");
					}
					i++;
				}
			}

		}
		CoreVars.uninit(this);
		LogManager lman = LogManager.getLogManager();
		lman.reset();
		if (errMess.length() > 0) {
			throw new InitException(errMess.toString());
		}
	}
}
